package com.ideaaedi.log4j2.defender.core;

import com.ideaaedi.log4j2.defender.defender.Log4j2MessageDefender;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.pattern.ConverterKeys;
import org.apache.logging.log4j.core.pattern.LogEventPatternConverter;
import org.apache.logging.log4j.core.pattern.MessagePatternConverter;
import org.apache.logging.log4j.core.pattern.PatternConverter;
import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.util.PerformanceSensitive;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

/**
 * Returns the event's rendered message in a StringBuilder.
 * <p>
 * 注：log4j2在加载配置时，会根据log4j2.xml中配置的日志输出模板中的占位符，(会通过@ConverterKeys注解设
 *    置的占位符)来选择对应的Converter。所以这里我们设置Log4j2CoreConverter对应
 *    的key为{"defender_message", "def_msg"}, 只要在配置   log4j2.xml中的日志输出模板中时，
 *    占位符对应上即可。
 * <p>
 * 更多可参考{@link MessagePatternConverter}
 * </p>
 *
 * @author JustryDeng
 * @since 2021/7/21 1:15:15
 */
@PerformanceSensitive("allocation")
@ConverterKeys({"defender_message", "def_msg"})
@Plugin(name = "Log4j2MessageConverter", category = PatternConverter.CATEGORY)
public class Log4j2CoreConverter extends LogEventPatternConverter {
    
    private static final Logger LOGGER = LoggerFactory.getLogger(Log4j2CoreConverter.class);

    /** 脱敏处理器 */
    private static Log4j2MessageDefender defender;
    
    /**
     * 调试模式
     * <br/>
     * 注：只有在调试模型下，才会打印脱敏过程中，可能出现的错误。详见{@link Log4j2CoreConverter#format(LogEvent, StringBuilder)} )
     */
    private static boolean debug = false;
    
    /**
     * @see DefenderConfiguration#getIncludeLoggerPrefix()
     */
    private static final Set<String> INCLUDE_PACKAGE_PREFIX = new HashSet<>();
    
    /**
     * @see DefenderConfiguration#getExcludeLoggerPrefix()
     */
    private static final Set<String> EXCLUDE_PACKAGE_PREFIX = new HashSet<>();

    /**
     * Private constructor.
     *
     * @param options
     *         options, may be null.
     */
    @SuppressWarnings("unused")
    private Log4j2CoreConverter(final Configuration config, final String[] options) {
        // 第一个参数为: 当前转换器的name,自定义即可
        // 第二个参数为: 输出内容的css样式, 为message即可
        super("defender-message", "message");
    }

    /**
     * Obtains an instance of pattern converter.
     *
     * @param config
     *         The Configuration.
     * @param options
     *         options, may be null.
     * @return instance of pattern converter.
     */
    @SuppressWarnings("unused")
    public static Log4j2CoreConverter newInstance(final Configuration config, final String[] options) {
        return new Log4j2CoreConverter(config, options);
    }

    @Override
    public void format(final LogEvent event, final StringBuilder toAppendTo){
        Message message = event.getMessage();
        if (message == null) {
            return;
        }
        // loggerName形如 org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter, 即:创建logger时所指定的Class的全类名
        String loggerName = event.getLoggerName();
        // 不需要脱敏
        boolean skip = defender == null || EXCLUDE_PACKAGE_PREFIX.stream().anyMatch(loggerName::startsWith);
        String formattedMessage = message.getFormattedMessage();
        if (skip) {
            toAppendTo.append(formattedMessage);
            return;
        }
        boolean needDesensitize = INCLUDE_PACKAGE_PREFIX.stream().anyMatch(loggerName::startsWith);
        if (needDesensitize) {
            // 需要脱敏
            int length = toAppendTo.length();
            try {
                defender.desensitize(event, message, toAppendTo);
            } catch (Throwable e) {
                if (debug) {
                    LOGGER.error("\nLog4j2CoreConverter occur exception."
                            + "\n\tloggerName: {}\n\tformattedMessage: {}",
                            loggerName, formattedMessage, e);
                }
                toAppendTo.setLength(length);
                toAppendTo.append(formattedMessage);
            }
        } else {
            // 不需要脱敏
            toAppendTo.append(formattedMessage);
        }
    }
    
    static void setDebug(boolean debug) {
        Log4j2CoreConverter.debug = debug;
    }
    
    static void setDefender(Log4j2MessageDefender defender) {
        Log4j2CoreConverter.defender = defender;
    }
    
    static void addIncludePackagePrefix(Collection<String> c) {
        Log4j2CoreConverter.INCLUDE_PACKAGE_PREFIX.addAll(c);
    }
    
    static void addExcludePackagePrefix(Collection<String> c) {
        Log4j2CoreConverter.EXCLUDE_PACKAGE_PREFIX.addAll(c);
    }
}